package com.chebaohui.iqs.service.impl.deal.union.sdk;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;

import com.chebaohui.iqs.service.impl.deal.union.entity.Union;

import java.security.cert.X509Certificate;

/**
 * 银联验证签名
 * 
 * @author xielonghai
 *
 */
public class UnionValidate {

	/**
	 * 验证签名-外部提供
	 * 
	 * @param reqData
	 * @param encoding
	 * @param union
	 * @return
	 */
	public static boolean validates(Map<String, String> reqData, String encoding, Union union) {
		reqData = filterBlank(reqData);
		UnionCert cert = new UnionCert(union);
		return validate(reqData, encoding, cert);
	}

	/**
	 * 过滤请求报文中的空字符串或者空字符串
	 * 
	 * @param contentData
	 * @return
	 */
	public static Map<String, String> filterBlank(Map<String, String> contentData) {
		LogUtil.writeLog("打印请求报文域 :");
		Map<String, String> submitFromData = new HashMap<String, String>();
		Set<String> keyset = contentData.keySet();

		for (String key : keyset) {
			String value = contentData.get(key);
			if (StringUtils.isNotBlank(value)) {
				// 对value值进行去除前后空处理
				submitFromData.put(key, value.trim());
				LogUtil.writeLog(key + "-->" + String.valueOf(value));
			}
		}
		return submitFromData;
	}

	/**
	 * 验证签名
	 * 
	 * @param resData
	 *            返回报文数据
	 * @param encoding
	 *            编码格式
	 * @return
	 */
	private static boolean validate(Map<String, String> resData, String encoding, UnionCert cert) {
		LogUtil.writeLog("验签处理开始");
		if (null == encoding || "".equals(encoding.trim())) {
			encoding = "UTF-8";
		}
		String signMethod = resData.get(ProKey.param_signMethod);
		String version = resData.get(ProKey.param_version);
		if (ProKey.SIGNMETHOD_RSA.equals(signMethod) || ProKey.VERSION_1_0_0.equals(version)
				|| ProKey.VERSION_5_0_1.equals(version)) {
			// 获取返回报文的版本号
			if (ProKey.VERSION_5_0_0.equals(version) || ProKey.VERSION_1_0_0.equals(version)
					|| ProKey.VERSION_5_0_1.equals(version)) {
				String stringSign = resData.get("signature");
				LogUtil.writeLog("签名原文：[" + stringSign + "]");
				// 从返回报文中获取certId ，然后去证书静态Map中查询对应验签证书对象
				String certId = resData.get("certId");
				LogUtil.writeLog("对返回报文串验签使用的验签公钥序列号：[" + certId + "]");
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(resData);
				LogUtil.writeLog("待验签返回报文串：[" + stringData + "]");
				try {
					// 验证签名需要用银联发给商户的公钥证书.
					return SecureUtil.validateSignBySoft(cert.getValidatePublicKey(certId),
							SecureUtil.base64Decode(stringSign.getBytes(encoding)),
							SecureUtil.sha1X16(stringData, encoding));
				} catch (UnsupportedEncodingException e) {
					LogUtil.writeErrorLog(e.getMessage(), e);
				} catch (Exception e) {
					LogUtil.writeErrorLog(e.getMessage(), e);
				}
			} else if (ProKey.VERSION_5_1_0.equals(version)) {
				// 1.从返回报文中获取公钥信息转换成公钥对象
				String strCert = resData.get("signPubKeyCert");
				// LogUtil.writeLog("验签公钥证书：["+strCert+"]");
				X509Certificate x509Cert = cert.genCertificateByStr(strCert);
				if (x509Cert == null) {
					LogUtil.writeErrorLog("convert signPubKeyCert failed");
					return false;
				}
				// 2.验证证书链
				if (!cert.verifyCertificate(x509Cert)) {
					LogUtil.writeErrorLog("验证公钥证书失败，证书信息：[" + strCert + "]");
					return false;
				}

				// 3.验签
				String stringSign = resData.get("signature");
				LogUtil.writeLog("签名原文：[" + stringSign + "]");
				// 将Map信息转换成key1=value1&key2=value2的形式
				String stringData = coverMap2String(resData);
				LogUtil.writeLog("待验签返回报文串：[" + stringData + "]");
				try {
					// 验证签名需要用银联发给商户的公钥证书.
					boolean result = SecureUtil.validateSignBySoft256(x509Cert.getPublicKey(),
							SecureUtil.base64Decode(stringSign.getBytes(encoding)),
							SecureUtil.sha256X16(stringData, encoding));
					LogUtil.writeLog("验证签名" + (result ? "成功" : "失败"));
					return result;
				} catch (UnsupportedEncodingException e) {
					LogUtil.writeErrorLog(e.getMessage(), e);
				} catch (Exception e) {
					LogUtil.writeErrorLog(e.getMessage(), e);
				}
			}

		} else if (ProKey.SIGNMETHOD_SHA256.equals(signMethod)) {
			// 1.进行SHA256验证
			String stringSign = resData.get("signature");
			LogUtil.writeLog("签名原文：[" + stringSign + "]");
			// 将Map信息转换成key1=value1&key2=value2的形式
			String stringData = coverMap2String(resData);
			LogUtil.writeLog("待验签返回报文串：[" + stringData + "]");
			String strBeforeSha256 = stringData + "&" + SecureUtil.sha256X16Str(null, encoding);
			String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256, encoding);
			boolean result = stringSign.equals(strAfterSha256);
			LogUtil.writeLog("验证签名" + (result ? "成功" : "失败"));
			return result;
		} else if (ProKey.SIGNMETHOD_SM3.equals(signMethod)) {
			// 1.进行SM3验证
			String stringSign = resData.get("signature");
			LogUtil.writeLog("签名原文：[" + stringSign + "]");
			// 将Map信息转换成key1=value1&key2=value2的形式
			String stringData = coverMap2String(resData);
			LogUtil.writeLog("待验签返回报文串：[" + stringData + "]");
			String strBeforeSM3 = stringData + "&" + SecureUtil.sm3X16Str(null, encoding);
			String strAfterSM3 = SecureUtil.sm3X16Str(strBeforeSM3, encoding);
			boolean result = stringSign.equals(strAfterSM3);
			LogUtil.writeLog("验证签名" + (result ? "成功" : "失败"));
			return result;
		}
		return false;
	}

	/**
	 * 将Map中的数据转换成key1=value1&key2=value2的形式 不包含签名域signature
	 * 
	 * @param data
	 *            待拼接的Map数据
	 * @return 拼接好后的字符串
	 */
	public static String coverMap2String(Map<String, String> data) {
		TreeMap<String, String> tree = new TreeMap<String, String>();
		Iterator<Entry<String, String>> it = data.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			if (ProKey.param_signature.equals(en.getKey().trim())) {
				continue;
			}
			tree.put(en.getKey(), en.getValue());
		}
		it = tree.entrySet().iterator();
		StringBuffer sf = new StringBuffer();
		while (it.hasNext()) {
			Entry<String, String> en = it.next();
			sf.append(en.getKey() + ProKey.EQUAL + en.getValue() + ProKey.AMPERSAND);
		}
		return sf.substring(0, sf.length() - 1);
	}
}
